﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32.SafeHandles; 
using System.Runtime.InteropServices; 
using System.Threading;
using GenericHid;

namespace ibuddylib {

    /// <summary>
    /// A UsbBuddy represents a physical, USB-connected i-Buddy
    /// </summary>
    public class UsbBuddy : IBuddy, IDisposable {
        private string usbPath;
        private SafeFileHandle handle;
        private WingPosition wingPosition = WingPosition.Low;
        private HeadColor headColor = HeadColor.Off;
        private HeartLight heartLight = HeartLight.Off;
        private TorsoYaw torsoYaw = TorsoYaw.Middle;
        private bool changed;
        private bool updateDelayed;

        internal UsbBuddy(string usbPath, SafeFileHandle handle) {
            this.usbPath = usbPath;
            this.handle = handle;
            changed = false;
            updateDelayed = false;
        }

        internal string UsbPath { 
            get {
                return usbPath;
            }
        }

        public void Dispose() {
            lock(handle)
                handle.Close();
        }

        /// <summary>
        /// Send the given control code to the i-Buddy
        /// </summary>
        /// <param name="code"></param>
        private void Send(byte code) {
            byte[] buffer = new byte[9]; 

            // This stuff is the same for every call
            buffer[0] = 0;
            buffer[1] = 0x55;
            buffer[2] = 0x53;
            buffer[3] = 0x42;
            buffer[4] = 0x43;
            buffer[5] = 0x00;
            buffer[6] = 0x40;
            buffer[7] = 0x02;

            // Here is the actual code we're sending
            buffer[8] = code;

            Hid.OutputReportViaControlTransfer outputReport = new Hid.OutputReportViaControlTransfer(); 

            lock(handle)
                outputReport.Write(buffer, handle); 
        }

        /// <summary>
        /// Calculate the new control code to be sent to the USB buddy and send it
        /// </summary>
        private void SendBuddyControl() {
            if (!changed) return;
            
            byte control = 0;

            if (TorsoYaw == TorsoYaw.Left) control += 1;
            else if (TorsoYaw == TorsoYaw.Right) control += 2;
            else control += 0;

            if (WingPosition == WingPosition.Low) control += 2 << 2;
            else control += 1 << 2;

            if (HeadColor == HeadColor.Off) control += 7 << 4;
            else control += (byte)((((byte)HeadColor) - 1) << 4);

            if (HeartLight == HeartLight.Off) control += 1 << 7;

            Send(control);
            changed = false;
        }



        #region IBuddy Members

        public WingPosition WingPosition {
            get {
                return wingPosition;
            }
            set {
                if (wingPosition != value) {
                    changed = true;
                    wingPosition = value;
                    if (!updateDelayed) SendBuddyControl();
                }
            }
        }

        public HeadColor HeadColor {
            get {
                return headColor;
            }
            set {
                if (headColor != value) {
                    changed = true;
                    headColor = value;
                    if (!updateDelayed) SendBuddyControl();
                }
            }
        }

        public HeartLight HeartLight {
            get {
                return heartLight;
            }
            set {
                if (heartLight != value) {
                    changed = true;
                    heartLight = value;
                    if (!updateDelayed) SendBuddyControl();
                }
            }
        }

        public TorsoYaw TorsoYaw {
            get {
                return torsoYaw;
            }
            set {
                if (torsoYaw != value) {
                    changed = true;
                    torsoYaw = value;
                    if (!updateDelayed) SendBuddyControl();
                }
            }
        }

        public void BeginUpdate() {
            updateDelayed = true;
        }

        public void EndUpdate() {
            updateDelayed = false;
            SendBuddyControl();
        }

        #endregion
    }
}
